/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package databook.controller;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import javax.swing.*;
import java.awt.*;

import prefuse.util.ColorLib;

/**
 *
 * @author zach
 */
public class Props {
    private static HashMap<String,Object> map;
    public static final String DELIM = ":";
    
    protected static final String FILTER = "FILTER";
    protected static final String COLOR = "COLOR";
    
    protected static HashMap<String,Vector> propListeners;
    protected static Vector<PropertyListener> filterListeners;
    
    protected static Vector<String> noload;
    
    private static Pattern integer = Pattern.compile("-?\\d+");
    private static Pattern doub = Pattern.compile("-?[.\\d]+");

    
    public static void load(String filename) throws FileNotFoundException,IOException {
        load(new File(filename));
    }
    
    public static void load(File file) throws FileNotFoundException, IOException {
        BufferedReader in = new BufferedReader(new FileReader(file));
        String input;
        while ((input = in.readLine()) != null) {
            String[] parts = input.split("\\s*=\\s*",2);
            if(parts.length == 2 && !noload.contains(parts[0])) {
                Object type = getType(parts[1]);
                System.out.println(parts[0] + " to " + type);
                set(parts[0],type);
            }
        }
        in.close();
    }
    
    public static Object getType(String value) {
        if (value.toUpperCase().equals("TRUE") || value.toUpperCase().equals("FALSE")) {
            return new Boolean(value);
        } else if (integer.matcher(value).matches()) {
            return new Integer(value);
        } else if (doub.matcher(value).matches()) {
            return new Double(value);
        } else 
            return value;
        
    }
    
    public static void save(String filename) throws IOException {
        save(new File(filename));
    }
    
    public static void save(File file) throws IOException {
        PrintWriter out = new PrintWriter(file);
        for (Entry entry: map.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
            out.println(entry.getKey() + "=" + entry.getValue());
        }
        out.close();
    }
    
    public static void addListener(String key, PropertyListener l) {
        if (!propListeners.containsKey(key)) {
            propListeners.put(key,new Vector<PropertyListener>());
        }
        propListeners.get(key).add(l);
        //if (key.indexOf("GENDER") != -1) System.out.println("\t\tSize: " + propListeners.get(key).size() + " " + key);
    }
    
    public static void addListener(String[] keys, PropertyListener l) {
        for (String key : keys) {
            addListener(key,l);
        }
    }
    
    public static void removeListener(String key, PropertyListener l) {
        if (propListeners.containsKey(key)) {
            propListeners.get(key).remove(l);
        }
    }
    
    public static void nofityListeners(String key) {
        if (propListeners.containsKey(key)) {
            Vector<PropertyListener> listeners = propListeners.get(key);
            for (PropertyListener list : listeners) {
                list.propertyChanged(new PropertyEvent(key,get(key)));
            }
        }
    }
    
    public static void notifyFilterListeners(String key, boolean val) {
        for (PropertyListener list : filterListeners) {
            list.propertyChanged(new PropertyEvent(key,new Boolean(val)));
        }
    }
    
    public static void addFilterListener(String key, String val, PropertyListener l) {
        addListener(getFilterKey(key,val),l);
    }
    
    public static void addFilterListener(PropertyListener l) {
        filterListeners.add(l);
    }
    
    public static void removeFilterListener(PropertyListener l) {
        filterListeners.remove(l);
    }
   
    public static void set(String key, Object val) {
        map.put(key,val);
        nofityListeners(key);
    }
    
    public static void set(String key, int val) {
        set(key,new Integer(val));
    }
    
    public static void set(String key, boolean val) {
        set(key,new Boolean(val));
    }
    
    public static void set(String key, double val) {
        set(key,new Double(val));
    }
    
    public static void setDefault(String key, Object val) {
        if (!map.containsKey(key)) {
            set(key,val);
        }
    }
    
    public static void setColor(String key, String val, int color) {
        set(getColorKey(key,val),color);
    }
    
    public static void setDefault(String key, int val) {
        setDefault(key,new Integer(val));
    }
    
    public static void setDefault(String key, boolean val) {
        setDefault(key,new Boolean(val));
    }
    
    public static void setDefault(String key, double val) {
        setDefault(key,new Double(val));
    }
    
    public static Object get(String key) {
        return get(key,null);
    }
    
    public static Object get(String key, Object def) {
        if (!map.containsKey(key)) {
            return def;
        }
        return map.get(key);
    }
    
    public static String getString(String key) {
        return getString(key,"");
    }
    
    public static String getString(String key, String def) {
        return (String)get(key,def);
    }
    
    public static int getColor(String key, String val) {
        return getColor(getColorKey(key,val));
    }
    public static int getColor(String key) {
        return getColor(key,DEFAULT_COLOR);
    }
    
    public static int getColor(String key, int def) {
        return getInt(key,def);
    }
    
    public static int getInt(String key) {
        return getInt(key,-1);
    }
    
    public static int getInt(String key, int def) {
        return ((Integer)get(key,new Integer(def))).intValue();
    }
    
    public static boolean getBoolean(String key) {
        return getBoolean(key,false);
    }
    
    public static boolean getBoolean(String key, boolean def) {
        return ((Boolean)get(key,new Boolean(def))).booleanValue();
    }
    
    public static double getDouble(String key) {
        return getDouble(key,-1);
    }
    
    public static double getDouble(String key, double def) {
        return ((Double)get(key,new Double(def))).doubleValue();
    }
    
    public static boolean filter(String key, String val) {
        return getBoolean(getFilterKey(key,val),true);
    }
    
    public static void setFilter(String key, String val, boolean b) {
        setFilter(key,val,b,true);
    }
    
    public static void setFilter(String key, String val, boolean b, boolean notify) {
        set(getFilterKey(key,val),b);
        if (notify)
            notifyFilterListeners(getFilterKey(key,val),b);
    }
    
    public static String getFilterKey(String key, String val) {
        return FILTER + DELIM + key + DELIM + val;
    }
    
    public static String getColorKey(String key, String val) {
        return COLOR + DELIM + key + DELIM + val;
    }
    
    public static JPanel getLegend() {
        JPanel outer = new JPanel();
        outer.setAlignmentX(outer.LEFT_ALIGNMENT);
        outer.setLayout(new GridLayout(0,1));
        
        outer.add(new JLabel("Nodes"));
        outer.add(getLegendItem(Props.DEFAULT_NODE,"Unselected Node","Default color of a node"));
        outer.add(getLegendItem(Props.SELECTED_NODE,"Selected Node","Color of a node that is selected"));
        outer.add(getLegendItem(Props.SELECTED_SOURCE_NODE,"Source of Selected Node","Means a node is a source of the selected node"));
        outer.add(getLegendItem(Props.SELECTED_TARGET_NODE,"Target of Selected Node","Means a node is a target of the selected node"));
        outer.add(getLegendItem(Props.SELECTED_T_S_NODE,"Source and Target of Selected Node","Means a node has edges going to and from the selected node."));
        
        outer.add(new JLabel("Edges"));
        outer.add(getLegendItem(Props.UNSELECTED_EDGE,"Default Edge","Default color of an edge"));
        outer.add(getLegendItem(Props.SELECTED_OUT_EDGE,"Out Going Edge","Means an edge flows out of the selected node"));
        outer.add(getLegendItem(Props.SELECTED_IN_EDGE,"In Coming Edge","Means an edge flows into the selected node"));
        
        
        return outer;
    }
    
    public static JPanel getLegend(String prop) {
        JPanel outer = new JPanel();
        outer.setAlignmentX(outer.LEFT_ALIGNMENT);
        outer.setLayout(new GridLayout(0,1));
    
        Iterator iter = map.keySet().iterator();
        Vector items = new Vector();
        while(iter.hasNext()) {
            String key = (String)iter.next();
            String prefix = COLOR + DELIM + prop + DELIM;
            if (key.startsWith(prefix)) {
                outer.add(getLegendItem(key,key.substring(prefix.length()),null));
            }
        }
        return outer;
    }
     
    public static JPanel getLegendItem(String colorProp, String label, String desc) {
        int color = Props.getColor(colorProp);
        JPanel item = new JPanel(new FlowLayout(FlowLayout.LEFT));
        item.setAlignmentX(item.LEFT_ALIGNMENT);
        JPanel spacer = new JPanel();
        spacer.setPreferredSize(new Dimension(13,13));
        spacer.setBackground(new Color(color,false));
        spacer.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        JLabel lab = new JLabel(label);
        lab.setToolTipText(desc);
        item.add(spacer);
        item.add(lab);
        return item;
    }
    
    //Common Keys.  Initialize them all to def, and let the static section set them
    public static String HIGHLIGHT_OUT = "def";
    public static String HIGHLIGHT_IN = "def";
    public static String NORMALIZE_SIZE = "def";
    public static String AUTO_LAYOUT = "def";
    public static String EDGE_THRESHHOLD = "def";
    public static String SELECTED_ITEM = "def";
    public static String CLICKED_EDGE = "def";
    public static String MAX_THRESHHOLD = "def";
    public static String PERCENT_IN = "def";
    public static String PERCENT_OUT = "def";
    public static String NUMBER_OF_TERMS = "def";
    public static String START_TERM = "def";
    public static String START_YEAR = "def";
    public static String END_TERM = "def";
    public static String END_YEAR = "def";
    public static String CURRENT_RANGE_MAX = "def";
    public static String CURRENT_RANGE_MIN = "def";
    public static String CURRENT_TERM = "def";
    
    
    private static int DEFAULT_COLOR = ColorLib.gray(155);
    
    public static final String DEFAULT_NODE = "Default Node";
    public static final String DEFAULT_NODE_TEXT = "Default Node Text";
    public static final String UNSELECTED_NODE = "Unselected Node";
    public static final String SELECTED_NODE = "Selected Node";
    public static final String SELECTED_NODE_BORDER = "Selected Node Border";
    public static final String UNSELECTED_NODE_BORDER = "Unselected Node Border";
    public static final String SELECTED_SOURCE_NODE = "Selected Source Node";
    public static final String SELECTED_T_S_NODE = "Selected T S Node";
    public static final String SELECTED_TARGET_NODE = "Selected Target Node";
    public static final String DEFAULT_START_EDGE = "Default Start Edge";
    public static final String DEFAULT_END_EDGE = "Default End Edge";
    public static final String UNSELECTED_EDGE = "Unselected Edge";
    public static final String SELECTED_OUT_EDGE = "Selected Out Edge";
    public static final String SELECTED_IN_EDGE = "Selected In Edge";
    public static String SHOW_X_AXIS_TEXT = "def";
    public static String Y_AXIS_STROKE_COLOR = "def";
    public static String Y_AXIS_TEXT_COLOR = "def";
    public static String X_AXIS_TEXT_COLOR = "def";
    
    static {
        map = new HashMap();
        filterListeners = new Vector<PropertyListener>();
        propListeners = new HashMap<String,Vector>();
        noload = new Vector();
        
        // Complete and utter lazyness!
        try {
            Field[] fields = Props.class.getDeclaredFields();
            Props tmp = new Props();
            for (int i = 0; i < fields.length; i++) {
                if (fields[i].get(tmp).equals("def")) {
                    fields[i].set(new Props(),fields[i].getName());
                }
            }  
        } catch (IllegalAccessException iae) {
            System.err.println(iae);
        }
        
        setDefault(PERCENT_IN,false);
        setDefault(PERCENT_OUT,false);
        setDefault(HIGHLIGHT_OUT,true);
        setDefault(HIGHLIGHT_IN,true);
        setDefault(AUTO_LAYOUT,true);
        setDefault(NORMALIZE_SIZE,true);
        
        setDefault(CURRENT_TERM,"2008-2");
        
        setDefault(EDGE_THRESHHOLD,0);
        
        setDefault(DEFAULT_NODE,ColorLib.rgb(190,190,255));
        setDefault(DEFAULT_NODE_TEXT,ColorLib.gray(0));
        setDefault(SELECTED_NODE,ColorLib.rgb(150,255,150));
        setDefault(SELECTED_TARGET_NODE,ColorLib.rgb(255,150,150));
        setDefault(SELECTED_SOURCE_NODE,ColorLib.rgb(255,255,150));
        setDefault(SELECTED_T_S_NODE,ColorLib.rgb(255,190,130));
        setDefault(UNSELECTED_NODE,ColorLib.rgb(190,190,255));
        setDefault(SELECTED_NODE_BORDER,ColorLib.gray(0));
        setDefault(UNSELECTED_NODE_BORDER,ColorLib.setAlpha(ColorLib.gray(255),255));
        
        setDefault(UNSELECTED_EDGE,ColorLib.hsb(.35f,1f,1f));
        setDefault(DEFAULT_START_EDGE,ColorLib.setAlpha(getColor(UNSELECTED_EDGE),10));
        setDefault(DEFAULT_END_EDGE,ColorLib.setAlpha(getColor(UNSELECTED_EDGE),255));
        
        setDefault(SELECTED_OUT_EDGE,ColorLib.hsba(.5f,1f,1f,1));
        setDefault(SELECTED_IN_EDGE,ColorLib.hsba(.75f,1f,1f,1));
        
        setDefault(Y_AXIS_STROKE_COLOR,ColorLib.gray(50));
        setDefault(Y_AXIS_TEXT_COLOR,ColorLib.gray(50));
        
            
        float stack_alpha = .65f;
        setColor("GENDER","M",ColorLib.rgba(.5f,.5f,1f,stack_alpha));
        setColor("GENDER","F",ColorLib.rgba(1f,.5f,.5f,stack_alpha));
        
        setColor("GRADUATED","Y",ColorLib.rgba(.3f,.9f,.3f,stack_alpha));
        setColor("GRADUATED","N",ColorLib.rgba(.9f,.3f,.3f,stack_alpha));
        setColor("GRADUATED","Not Yet",ColorLib.rgba(.3f,.3f,.9f,stack_alpha));
        setColor("RACE","Caucasian",ColorLib.rgba(.3f,.9f,.3f,stack_alpha));
        setColor("RACE","American Indian",ColorLib.rgba(.3f,.3f,.9f,stack_alpha));
        setColor("RACE","Black",ColorLib.rgba(.9f,.3f,.9f,stack_alpha));
        setColor("RACE","Hispanic",ColorLib.rgba(0f,.6f,1f,stack_alpha));
        setColor("RACE","Asian",ColorLib.rgba(1f,.6f,0f,stack_alpha));
        setColor("RACE","Unknown",ColorLib.rgba(.9f,.3f,.3f,stack_alpha));
        
        noload.add(SELECTED_ITEM);
        noload.add(CLICKED_EDGE);
    }
    
    // Lazy Function
    public static boolean usePercent() {
        return getBoolean(PERCENT_IN) || getBoolean(PERCENT_OUT);
    }
    
}
